#pragma once
#include <iostream>
#include <string>
#include <strings.h>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstdlib>
#include <cstring>
namespace Server
{
    const static std::string defaultIp = "0.0.0.0"; // 缺省ip
    const static int GNUM = 1024;
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        OPEN_ERR
    }err;
    class udpServer
    {

        using func_t = std::function<void(int, std::string, uint16_t, std::string)>;
    public:
        udpServer(const func_t& callback, const uint16_t& port, const std::string& ip = defaultIp)
            :_port(port)
            ,_ip(ip)
            ,_sockfd(-1)
            ,_callback(callback) // udpServer.cc传入的处理客户端数据的函数
        {}
        void initServer()
        {
            // 1、创建套接字
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);// 网络通信 + 数据报
            if (-1 == _sockfd)
            {
                std::cout << "socket error " << errno << ": " << strerror(errno) << std::endl;
                exit(SOCKET_ERR);                
            }
            std::cout << "socket success: " << _sockfd << std::endl;
            // 2、绑定ip和端口号
            struct sockaddr_in local;
            bzero(&local, sizeof(local));// 将一段内存初始化成0
            local.sin_family = AF_INET;// 设置协议族
            local.sin_port = htons(_port);// 设置端口号，需要转大端，主机转网络字节序
            local.sin_addr.s_addr = inet_addr(_ip.c_str());// 将ip字符串转为uint32_t并同时转换为网络字节序
            // local.sin_addr.s_addr = htonl(INADDR_ANY);// INADDR_ANY就是0，表明任何ip都可以访问这个服务器的_port端口
            int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
            if (-1 == n)
            {
                std::cout << "bind error " << errno << ": " << strerror(errno) << std::endl;
                exit(BIND_ERR);
            }

        }
        void start()
        {
            char buffer[GNUM];
            while (1)
            {
                struct sockaddr_in local;
                socklen_t len = sizeof(local);
                size_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)&local, &len);// 阻塞式读取
                // 这里需要关心1、数据是什么2、数据是谁发的
                if (n > 0)
                {
                    buffer[n] = '\0';
                    // 1、这是从网络读出来的IP，需要由网络字节序转主机字节序
                    // 2、整数转点分十进制IP,用inet_ntoa进行转换
                    // 将32位IPv4地址（in_addr结构体）转换成点分十进制字符串形式的IP地址
                    std::string clientIp = inet_ntoa(local.sin_addr); 
                    uint16_t clientPort = ntohs(local.sin_port); // 一样需要转换字节序
                    std::string message = buffer;
                    std::cout << clientIp << "[" << clientPort << "]#" << message << std::endl;
                    // 对数据进行处理
                    _callback(_sockfd, clientIp, clientPort, message);
                }
            }
        }
        ~udpServer()
        {}
    private:
        uint16_t _port; // 端口号
        std::string _ip; // ip地址
        int _sockfd; // 套接字文件描述符
        func_t _callback;// 回调函数
    };
}